#include "StdAfx.h"
#include "RegionObject.h"
#include "PathInfo.h"


namespace dhis
{
	CNeighbor::CNeighbor()
	{
		m_id = std::numeric_limits<int>::max();
		m_numpix = 0;
		m_dissimCrit = -std::numeric_limits<REAL>::epsilon();
		m_mergedPerim = 0.0;
	}

	CNeighbor::CNeighbor(const CNeighbor& o)
	{
		m_id = o.m_id;
		m_numpix = o.m_numpix;
		m_dissimCrit = o.m_dissimCrit;
		m_mergedPerim = o.m_mergedPerim;
	}

	CNeighbor& CNeighbor::operator=(const CNeighbor& o)
	{
		if (this != &o)
		{
			m_id = o.m_id;
			m_numpix = o.m_numpix;
			m_dissimCrit = o.m_dissimCrit;
			m_mergedPerim = o.m_mergedPerim;
		}
		return *this;
	}

	//---------------------------------------------------------------
	void _mergeCopy(std::list<CBoundaryLine>& src,BoundaryLineVec& dst,int&minx,int&maxx)
	{
		CBoundaryLine tmp;
		dst.clear();
		minx = std::numeric_limits<int>::max();
		maxx = std::numeric_limits<int>::min();
		while (!src.empty())
		{
			tmp = (*src.begin());
			src.erase(src.begin());
			while (!src.empty())
			{
				if ((tmp.m_row == src.begin()->m_row) && ((tmp.m_colEnd + 1) == src.begin()->m_colStart))
				{
					tmp.m_colEnd = src.begin()->m_colEnd;
					src.erase(src.begin());
				} else { break; }
			}
			if (tmp.m_colStart < minx) { minx = tmp.m_colStart; }
			if (tmp.m_colEnd > maxx) { maxx = tmp.m_colEnd; }
			dst.push_back(tmp);
		}   
	}

	//---------------------------------------------------------------
 	void _mergeBoundaryline(BoundaryLineVec& bounds,const BoundaryLineVec& other,int&minx,int&maxx)
	{
		std::list<CBoundaryLine> boundLins;
		for (int i = 0; i < bounds.size(); ++i) { boundLins.push_back(bounds[i]); }
		for (int i = 0; i < other.size(); ++i) { boundLins.push_back(other[i]); }
		boundLins.sort();
		_mergeCopy(boundLins,bounds,minx,maxx);
 	}

	void _mergeBoundaryline(const BoundaryLineVec& lhs,const BoundaryLineVec& rhs,BoundaryLineVec& bounds,int&minx,int&maxx)
	{
		std::list<CBoundaryLine> boundLins;
		for (int i = 0; i < lhs.size(); ++i) { boundLins.push_back(lhs[i]); }
		for (int i = 0; i < rhs.size(); ++i) { boundLins.push_back(rhs[i]); }
		boundLins.sort();
		_mergeCopy(boundLins,bounds,minx,maxx);
	}

	//---------------------------------------------------------------
	void _getUpperRowBounds(const BoundaryLineVec& bounds,const int& idx,std::vector<int>& upperRowBounds)
	{
		upperRowBounds.clear();
		if (idx == 0) { return; }
		int nUpperRow = bounds[idx].m_row-1;
		for (int i = idx-1; i >= 0; --i)
		{
			if (bounds[i].m_row < nUpperRow) { break; }
			if (bounds[i].m_row == nUpperRow) { upperRowBounds.push_back(i); }
		}
	}

	//---------------------------------------------------------------
	void _getUnderRowBounds(const BoundaryLineVec& bounds,const int& idx,std::vector<int>& underRowBounds)
	{
		underRowBounds.clear();
		if (idx == bounds.size()-1) { return; }
		int nUnderRow = bounds[idx].m_row+1;
		for (int i = idx+1; i < bounds.size(); ++i)
		{
			if (bounds[i].m_row > nUnderRow) { break; }
			if (bounds[i].m_row == nUnderRow) { underRowBounds.push_back(i); }
		}
	}

	//---------------------------------------------------------------
	bool _containsPixel(const BoundaryLineVec& bounds,std::vector<int>& nBounds,const int& row,const int& col)
	{
		if (nBounds.empty()) { return false; }
		for (int i = 0; i < nBounds.size(); ++i) { if (bounds[nBounds[i]].Contains(col,row)) { return true; } }
		return false;
	}

	//---------------------------------------------------------------
	REAL _calculatePerimeter(const BoundaryLineVec& bounds)
	{
		int i;
		REAL  perimeter = 0.0;
		if (bounds.size() == 1)
		{
			perimeter += 2.0;
			perimeter += 2.0*(bounds[0].m_colEnd-bounds[0].m_colStart+1);
			return perimeter;
		}

		std::vector<int> perims(bounds.size(),0);
#pragma omp parallel for shared(perims)// private(i)
		for (i = 0; i < bounds.size(); ++i)
		{
			const CBoundaryLine& bound = bounds[i];
			std::vector<int>  upperRowBounds,underRowBounds;
			perims[i] += 2.0;
			int nUpperRow = bound.m_row-1;
			int nUnderRow = bound.m_row+1;
			_getUpperRowBounds(bounds,i,upperRowBounds);
			_getUnderRowBounds(bounds,i,underRowBounds);

			for (int j = bound.m_colStart; j <= bound.m_colEnd; ++j)
			{
				if (!_containsPixel(bounds,upperRowBounds,nUpperRow,j)) { perims[i] += 1.0; }
				if (!_containsPixel(bounds,underRowBounds,nUnderRow,j)) { perims[i] += 1.0; }
			}
		}
		perimeter = std::accumulate(perims.begin(),perims.end(),0);
		return perimeter;
	}

	//---------------------------------------------------------------
	void _mergeObject(CRegionObject* obj, const CRegionObject* other)
	{
		assert(other->GetId() != obj->GetId());
		obj->NumPixel() = (obj->NumPixel()+other->NumPixel());

		for (int i = 0; i < other->GetSum().size(); ++i) 
		{
			obj->GetSum()[i] += other->GetSum()[i];
			obj->GetSumsq()[i] += other->GetSumsq()[i];
		}

		obj->DeleteNeighbor(other->GetId());
		for(NeighborList::const_iterator pos = other->GetNeighbors().begin(); pos != other->GetNeighbors().end(); pos++)
		{
			if(pos->m_id == obj->GetId()) continue;
			if (!obj->IsExistNeighbor(*pos)) { obj->AddNeighbor(*pos); }
		}

		int minx,maxx;
		_mergeBoundaryline(obj->GetBounds(),other->GetBounds(),minx,maxx);
		obj->MinX() = minx;
		obj->MaxX() = maxx;
	}

	//---------------------------------------------------------------
	void _pushBackPixelObject(CRegionObject* obj, const CRegionObject* other)
	{
		if (other->NumPixel() != 1) { return; }
		assert(other->GetId() != obj->GetId());
		obj->NumPixel() += other->NumPixel();

		for (int i = 0; i < other->GetSum().size(); ++i)
		{
			obj->GetSum()[i] += other->GetSum()[i];
			obj->GetSumsq()[i] += other->GetSumsq()[i];
		}

		BoundaryLineVec& bounds = obj->GetBounds();
		const CBoundaryLine& pixbound = other->GetBounds()[0];
		if ( (bounds.rbegin()->m_row == pixbound.m_row) && ((bounds.rbegin()->m_colEnd+1) == pixbound.m_colEnd) ) 
		{
			bounds.rbegin()->m_colEnd = pixbound.m_colEnd;
		} else {
			bounds.push_back(pixbound);
		}

		if (obj->MaxX() < pixbound.m_colEnd) { obj->MaxX() = pixbound.m_colEnd; }
	}
	
	//------------------------------------------------------------------------------
	CRegionObject::CRegionObject()
	{
		m_id = -1;
		m_numpix = 0;
		m_utilFlag = false;
		m_minx = std::numeric_limits<int>::max();
		m_maxx = std::numeric_limits<int>::min();
		m_avgDissim = 0.0;
		m_perimeter = 0.0;
	}
	
	CRegionObject::CRegionObject(const int & id)
	{
		m_id = id;
		m_numpix = 0;
		m_utilFlag = false;
		m_minx = std::numeric_limits<int>::max();
		m_maxx = std::numeric_limits<int>::min();
		m_avgDissim = 0.0;
		m_perimeter = 0.0;
	}

	CRegionObject::CRegionObject(const CRegionObject& other)
	{
		m_id = other.m_id;
		m_numpix = other.m_numpix;
		m_sum = other.m_sum;
		m_sumsq = other.m_sumsq;
		m_neighbors = other.m_neighbors;
		m_bounds = other.m_bounds;
		m_utilFlag = other.m_utilFlag;
		m_minx = other.m_minx;
		m_maxx = other.m_maxx;
		m_avgDissim = other.m_avgDissim;
		m_perimeter = other.m_perimeter;
	}

	CRegionObject& CRegionObject::operator=(const CRegionObject& other)
	{
		if (this != &other)
		{
			m_id = other.m_id;
			m_numpix = other.m_numpix;
			m_sum = other.m_sum;
			m_sumsq = other.m_sumsq;
			m_neighbors = other.m_neighbors;
			m_bounds = other.m_bounds;
			m_utilFlag = other.m_utilFlag;
			m_minx = other.m_minx;
			m_maxx = other.m_maxx;
			m_avgDissim = other.m_avgDissim;
			m_perimeter = other.m_perimeter;
		}
		return *this;
	}

	CRegionObject::~CRegionObject()
	{
	}

	bool CRegionObject::DeleteNeighbor(const int& nbid)
	{
		NeighborList::const_iterator pos = std::find(m_neighbors.begin(),m_neighbors.end(),nbid);
		if (m_neighbors.end() != pos)
		{
			m_neighbors.erase(pos);
			return true;
		}
		return false;
	}

	bool CRegionObject::DeleteNeighbor(const CNeighbor& nbr)
	{
		NeighborList::const_iterator pos = std::find(m_neighbors.begin(),m_neighbors.end(),nbr);
		if (m_neighbors.end() != pos)
		{
			m_neighbors.erase(pos);
			return true;
		}
		return false;
	}

	void CRegionObject::AddNeighbor(const CNeighbor& nbr)
	{
		m_neighbors.push_back(nbr);
	}

	void CRegionObject::Clear()
	{
		m_id = -1;
		m_numpix = 0;
		m_sum.clear();
		m_sumsq.clear();
		m_neighbors.clear();
		m_bounds.clear();
	}

	int CRegionObject::MinY() const
	{
		if (m_bounds.empty()) { return std::numeric_limits<int>::max(); }
		return m_bounds.begin()->m_row;
	}

	int CRegionObject::MaxY() const
	{
		if (m_bounds.empty()) { return std::numeric_limits<int>::min(); }
		return m_bounds.rbegin()->m_row;
	}

	void CRegionObject::CalculateAvgDissim()
	{
		m_avgDissim = 0.0;
		NeighborList::iterator pos;
		for (pos = m_neighbors.begin(); pos != m_neighbors.end(); ++pos)
		{
			m_avgDissim += pos->m_dissimCrit;
		}
		m_avgDissim = m_avgDissim/((REAL)m_neighbors.size());
	}

	void CRegionObject::CalculatePerimeter()
	{
		m_perimeter = 0.0;
		if (m_numpix == 1) {
			m_perimeter = 4.0; 
		} else if (m_numpix == 2) {
			m_perimeter = 6.0;
		} else if (m_numpix == 3) {
			m_perimeter = 8.0;
		} else {
			m_perimeter = _calculatePerimeter(m_bounds);
		}
	}

	//--------------------------------------------------------------------------
	CBoundaryLine::CBoundaryLine(void)
	{
		m_row = -1;
		m_colStart = -1;
		m_colEnd = -1;
	}

	CBoundaryLine::CBoundaryLine(const int& row,const int& colStart,const int& colEnd)
	{
		m_row = row;
		m_colStart = colStart;
		m_colEnd = colEnd;
	}

	CBoundaryLine::CBoundaryLine(const CBoundaryLine& other)
	{
		m_row = other.m_row;
		m_colStart = other.m_colStart;
		m_colEnd = other.m_colEnd;
	}

	CBoundaryLine& CBoundaryLine::operator=(const CBoundaryLine& other)
	{
		if (this != &other)
		{
			m_row = other.m_row;
			m_colStart = other.m_colStart;
			m_colEnd = other.m_colEnd;
		}
		return *this;
	}

}

